#include "sprite.h"

char *read_xpm(char* map[], int *wd, int *ht)
{
	__attribute__((unused)) static char read_xpm_jcard;

	int width, height, colors;
	char sym[256];
	int  col;
	int i, j;
	char *pix, *pixtmp, *tmp, *line;
	char symbol;

	// read width, height, colors
	if (sscanf(map[0],"%d %d %d", &width, &height, &colors) != 3) {
		printf("read_xpm: incorrect width, height, colors\n");
		return NULL;
	}
	#ifdef DEBUG
	printf("%d %d %d\n", width, height, colors);
	#endif
	if (colors > 256) {
		printf("read_xpm: incorrect width, height, colors\n");
		return NULL;
	}

	*wd = width;
	*ht = height;

	for (i=0; i<256; i++)
	sym[i] = 0;

	// read symbols <-> colors 
	for (i=0; i<colors; i++) {
		if (sscanf(map[i+1], "%c %d", &symbol, &col) != 2) {
			printf("read_xpm: incorrect symbol, color at line %d\n", i+1);
			return NULL;
		}
	#ifdef DEBUG
		printf("%c %d\n", symbol, col);
	#endif
		if (col > 256) {
			printf("read_xpm: incorrect color at line %d\n", i+1);
			return NULL;
		}
		sym[col] = symbol;
	}

	// allocate pixmap memory
	pix = pixtmp = malloc(width*height);

	// parse each pixmap symbol line
	for (i=0; i<height; i++) {
		line = map[colors+1+i];
	#ifdef DEBUG
		printf("\nparsing %s\n", line);
	#endif
		for (j=0; j<width; j++) {
			tmp = memchr(sym, line[j], 256);  // find color of each symbol
			if (tmp == NULL) {
				printf("read_xpm: incorrect symbol at line %d, col %d\n", colors+i+1, j);
				printf("symbol - %c (%d)\n", line[j], line[j]);
				return NULL;
			}
			*pixtmp++ = tmp - sym; // pointer arithmetic! back to books :-)
	#ifdef DEBUG
			printf("%c:%d ", line[j], tmp-sym);
	#endif
		}
	}

	return pix;
}





/****************************
*						    *
*		CREATE SPRITE		*
*						    *
****************************/

Sprite* create_sprite(char *pic[],char *base,int xi, int yi)
{ 
    if(pic == NULL || base == NULL) return NULL;
    
    Sprite* sprt = malloc(sizeof(Sprite));
    
    int wd,hg;
    sprt->map = read_xpm(pic,&wd,&hg);
    
    sprt->x = xi;
    sprt->y = yi;
    sprt->height = hg;
    sprt->width = wd;
    sprt->xspeed =rand()% MAX_SPEED*2 - MAX_SPEED;
    sprt->yspeed =rand()% MAX_SPEED*2 - MAX_SPEED;
    
    return sprt;
}


Player_Sprite * create_player_sprite(char *pic[],char * base, int xi, int yi)
{
    if(pic == NULL || base == NULL) return NULL;
    
    Player_Sprite* sprt = malloc(sizeof(Sprite));
    
    int wd,hg;
    sprt->map = read_xpm(pic,&wd,&hg);
    
    sprt->x = xi;
    sprt->y = yi;
    sprt->height = hg;
    sprt->width = wd;
    sprt->bgmap = malloc(sprt->width * sprt->height);  
     
    char* bgMap = sprt->bgmap;
    int i,k;
    for(i=0; i < sprt->height; i++)
   		for(k=0; k < sprt->width; k++)
   		    *(bgMap + sprt->width*i + k) = get_pixel(sprt->x+k, sprt->y+i, base); //guarda o valor antigo
    
    return sprt;
}



/****************************
*						    *
*		DRAW SPRITE		    *
*						    *
****************************/


void draw_sprite(Sprite *sprt,char *base)
{    
    int i,k;
    char* dSprt = sprt->map;
   	for(i=0; i < sprt->height; i++)
   		for(k=0; k < sprt->width; k++)
   		{
   		    if(*dSprt != 0)
   		        set_pixel(sprt->x+k,sprt->y+i, *(dSprt), base);  //actualiza com o valor actual
               dSprt++;
   		}
}

void draw_player_sprite(Player_Sprite *sprt,char *base)
{    
    char* dSprt = sprt->map;
	char* bgMap = sprt->bgmap;
	
	int i,k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
		{
			*(bgMap + sprt->width*i + k) = get_pixel(sprt->x+k, sprt->y+i, base); //guarda o valor antigo
			if(*dSprt != 0) set_pixel(sprt->x+k,sprt->y+i, *dSprt, base);  //actualiza com o valor actual
			dSprt++;
		}
}




/****************************
*						    *
*		DESTROY SPRITE		*
*						    *
****************************/



void destroy_sprite(Sprite *sprt, char *base)
{
	int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
			set_pixel(sprt->x+k,sprt->y+i, 0, base);
    
        free(sprt->map);
        free(sprt);
}

void destroy_player_sprite(Player_Sprite *sprt, char *base)
{
	int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
			set_pixel(sprt->x+k,sprt->y+i, 0, base);
    
        free(sprt->map);
        free(sprt->bgmap);
        free(sprt);
}




/****************************
*						    *
*		CLEAR SPRITE		*
*						    *
****************************/


void clean_screen_sprite(Sprite *sprt, char *base)
{
    int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
			set_pixel(sprt->x+k,sprt->y+i, 0, base);
}

void clean_screen_player_sprite(Player_Sprite *sprt, char *base)
{       
    int i, k;
    for(i=0; i < sprt->height; i++)
    for(k=0; k < sprt->width; k++)
        set_pixel(sprt->x+k,sprt->y+i, (int)*(sprt->bgmap + sprt->width*i + k), base);     
}





/****************************
*						    *
*		ANIMATE SPRITE		*
*						    *
****************************/

#define SIGN(x) ((x) >= 0 ? 1 : -1)

int
animate_sprite(Sprite *fig, char *base)
{
    Bool life = false;
  int xi, xf, yi, yf;
  int i, j, k, col;

  if (fig == NULL)
    return 0;

  xi = fig->x;
  yi = fig->y;

  
  //if (xi + fig->xspeed + fig->width > HRES || xi + fig->xspeed < 0)
//    fig->xspeed = -fig->xspeed; // colision with screen vertical borders
  xf = xi + fig->xspeed;

 // if (yi + fig->yspeed + fig->height > VRES || yi + fig->yspeed < 0)
 //   fig->yspeed = -fig->yspeed; // colision with screen horizontal borders
  yf = yi + fig->yspeed;
  

  // clear source
  clean_screen_sprite(fig, base);


  col = 0;
  // check for colision. lenghgly! detect on copy bellow?
  for (i=0; i<fig->height; i++)
    for (j=0; j<fig->width; j++)
      //if (*(base +  xf + j + (yf+i)*HRES) != BACK) { // general colision
	  if (get_pixel(xf + j, yf + i, base) != BACK) { // general colision
		col = 1;
		if(get_pixel(xf + j, yf + i, base) == RED_COLOR)
            life = true;
		//if (*(base +  xf + j + (yf+i)*HRES) == WHITE) { // block colision
		if (get_pixel(xf + j, yf + i, base) != BACK) { // block colision
			if (xf > HRES/2)
				col = RIGHT_HIT;
			else
				col = LEFT_HIT;
		//} else if (*(base +  xf + j + (yf+i)*HRES) == LIGHTMAGENTA) // mouse colision
		} else if (get_pixel(xf + j, yf + i, base) == LIGHTMAGENTA) // mouse colision
            col = MOUSE_HIT;
		goto end; // break two nested for
      }
 end:

    if(life == true)
        lose_life(base);
  // colision. Change velocity sign and magnitude in either direction 
  if (col != 0) {
      
    int xsg = -1 * SIGN(fig->xspeed); 
    int ysg = -1 * SIGN(fig->yspeed); 
    do {
      if ((rand() % 2) == 1)
		fig->xspeed = xsg * (rand() % MAX_SPEED);
      else
		fig->yspeed = ysg * (rand() % MAX_SPEED);
    } while (fig->xspeed == 0 || fig->yspeed == 0); // no zero speed!
  // repaint, leave update to next time frame
  k = 0;
  for (i=0; i<fig->height; i++)
    for (j=0; j<fig->width; j++)
        if (*(fig->map + k++) != BLACK)
           //*(base + xi + j + (yi + i) * HRES) = *(fig->map + k - 1);
		   set_pixel(xi + j, yi + i, *(fig->map + k - 1), base);
    return col;     // without painting
  }

  
  // copy pixmap to destination
   fig->x = xf;
   fig->y = yf;
   draw_sprite(fig,base);
   
   return col;
}


int move_player_sprite(Player_Sprite * sprt,char* base,int x, int y, char flag){
    
    clean_screen_player_sprite(sprt,base);
	if(flag != 'm')
		leave_trail(sprt,base);
    int xf,yf;
    xf = sprt->x + x;
    yf = sprt->y + y;
    
    if((xf+sprt->width)+1 > HRES){ // borda lado direito
        xf = HRES - sprt->width;
        sprt->x = xf;
    }
    if((yf+sprt->height) > VRES-1){ // borda inferior
        yf = VRES - sprt->height;
        sprt->y = yf;
    }
    if(xf < 0){ // borda lado esquerdo
        xf = 0;
        sprt->x = xf;
    }  
    if(flag == 'm' ? yf < 0 : yf < 156){ // borda superior
		if(flag == 'm')
			yf = 0;
		else
			yf = 156;
			
		sprt->y = yf;
		sprt->x = xf;
    }
    else{
            sprt->x = xf;
            sprt->y = yf;   
                    
            if(saida_sprite == false){
                    saida_sprite = saida(sprt->x,sprt->y,sprt->height,sprt->width,base);
                    
                    if(saida_sprite == true){
                        xini= sprt->x;
                        yini= sprt->y;
                    }
                    
             }      
            else{
                if(saida_sprite == true){
                    chegada_sprite = chegada(sprt->x,sprt->y,sprt->height,sprt->width,base);
                }    
            }
    }

    return 0;
}


/****************************
*					    	*
*		AUX         		*
*						    *
****************************/

int check_colision(Sprite *fig, char *base)
{
  int i, j;

  // make sure no object exists at the creation coordinates
  for (i=0; i<fig->height; i++)
    for (j=0; j<fig->width; j++)
      ///if (*(base +  fig->x + j + (fig->y+i)*HRES) != BACK) // colision
	  if (get_pixel(fig->x + j, fig->y + i, base) != BACK)
		return  1;

  return 0;
}



void drawRectangle(int x, int y, int colour, int height, int width, Bool filled, char* base){
	int i, j;
	for(i = 0; i < height; i++)
		for(j = 0; j < width; j++)
			if(filled)
				set_pixel(x+j, y+i, colour, base);
			else if( i == 0 || j == 0 || j == width - 1  || i == height - 1 )
				set_pixel(x+j, y+i, colour, base);
}

void leave_trail(Player_Sprite *sprt, char *base)
{
    int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++){
		    if(get_pixel(sprt->x+k,sprt->y+i,base) == BORDER_COLOR)
		        set_pixel(sprt->x+k,sprt->y+i, BORDER_COLOR, base);
		    else
			    set_pixel(sprt->x+k,sprt->y+i, RED_COLOR, base);
		}
}


Bool chegada(int xf,int yf,int hg, int wd, char* base){
     if(get_pixel(xf,yf,base) == BORDER_COLOR && get_pixel(xf,yf+hg-1,base) ==BORDER_COLOR && get_pixel(xf+wd-1,yf,base) == BORDER_COLOR && get_pixel(xf+wd-1,yf+hg-1,base) == BORDER_COLOR) 
        return true;
     else
         return false;
}

Bool saida(int xf,int yf,int hg, int wd, char* base){
     if(get_pixel(xf,yf,base) == 0 && get_pixel(xf,yf+hg-1,base) ==0 && get_pixel(xf+wd-1,yf,base) == 0 && get_pixel(xf+wd-1,yf+hg-1,base) == 0)        
        return true;
     else
         return false;     
}


void fillRegion(int xi, int yi, char* base){
	Stack pilha;
	initStack(&pilha, 100);
	push(&pilha, xi);
	push(&pilha, yi);
	while(!emptyStack(&pilha)){
		int y = top(&pilha); pop(&pilha);
		int x = top(&pilha); pop(&pilha);
		int i = 0;
		while(get_pixel(x+i, y, base) != BORDER_COLOR){
			set_pixel(x+i, y, BORDER_COLOR, base);
			i++;
		}
		int xright = x + (i - 1); i = 1;
		while(get_pixel(x-i, y, base) != BORDER_COLOR){
			set_pixel(x-i, y, BORDER_COLOR, base);
			i++;
		}
		int xleft = x - (i - 1); i = 0;
		while(xleft + i <= xright){
			if((get_pixel(xleft + i, y + 1, base) == 0 && get_pixel(xleft + i + 1, y + 1, base) == BORDER_COLOR) 
				|| (xleft + i == xright && get_pixel(xleft + i, y + 1, base) == 0)){
				push(&pilha, xleft + i);
				push(&pilha, y + 1);
			}
			i++;
		}
		i = 0;
		while(xleft + i <= xright){
			if((get_pixel(xleft + i, y - 1, base) == 0 && get_pixel(xleft + i + 1, y - 1, base) == BORDER_COLOR) 
				|| (xleft + i == xright && get_pixel(xleft + i, y - 1, base) == 0)){
				push(&pilha, xleft + i);
				push(&pilha, y - 1);
			}
			i++;
		}
	}
	freeStack(&pilha);
}

